package operator

import (
	"encoding/hex"
	"gitee.com/bupt-zkjc/fabric2.2-interface/common/json"
	"gitee.com/bupt-zkjc/fabric2.2-interface/common/log"
	"gitee.com/bupt-zkjc/fabric2.2-interface/config_yaml"
	"gitee.com/bupt-zkjc/fabric2.2-interface/constant"
	"gitee.com/bupt-zkjc/fabric2.2-interface/fasdk"
	"gitee.com/bupt-zkjc/fabric2.2-interface/model"
	"gitee.com/bupt-zkjc/fabric2.2-interface/util"
	"github.com/pkg/errors"
	"path/filepath"
)

var logger = log.GetLogger("fabric.Operator", log.INFO)

type FabricOperator struct {
}

func (f FabricOperator) addChannel(chain model.FabricChain)  (string, error){
	//Todo: 修改命令，进行参数化
	genTxCommand := "./bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/" + chain.ChannelName + constant.Tx + " -channelID " + chain.ChannelName
	err := fautil.Exec_shell(genTxCommand, chain.WorkPath)
	if err != nil{
		logger.Error(err)
		return "Execute add channel failure", err
	}
	logger.Info("Add channel successful!")
	return "Add channel success!", nil
}

//定义channel和构建
func (f FabricOperator) createChannel(chain model.FabricChain)  (string, error){

	fsdk := fasdk.NewFabricClient(chain.ChainName, chain.ChannelName, chain.PeersOrgs,
		chain.OrdererName, chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	err = fsdk.CreateChannel(fautil.GetChannelTx(chain))
	if err != nil {
		logger.Error(err)
		return "Create channel failure", err
	}
	command :=  constant.CreateChannel + constant.Separator + "config_" + chain.ChainName +
		constant.Yaml + constant.Separator + chain.ChannelName
	path, err := config_yaml.GetParentDirectory()
	if err != nil {
		return "Create channel failure", err
	}

	err = fautil.Exec_shell(command, filepath.Join(path, constant.Dirpath))
	if err != nil{
		logger.Error(err)
		return "Update yaml file failure", err
	}

	logger.Info("Create channel success!")
	//跟新锚节点
	//fsdk.UpdateChannel(fautil.GetAnchorsTxs(chain, paths.ArtifactPath))
	/*操作fabric end*/
	return "Success", nil
}

//定义channel和构建
func (f FabricOperator) joinPeer(channel model.FabricChannelWithOrg)  (string, error){
	fsdk := fasdk.NewFabricClient(channel.ChainName, channel.ChannelName, channel.PeersOrgs,
		channel.OrdererName, channel.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//跟新锚节点
	//fsdk.UpdateChannel(fautil.GetAnchorsTxs(chain, paths.ArtifactPath))
	err = fsdk.JoinChannel()
	if err != nil {
		logger.Error(err)
		return "Join channel failure", err
	}

	allPeersName := ""
	for _, peer := range channel.OrgPeers {
		allPeersName = allPeersName + constant.Separator +peer
	}

	// ./peerJoinChannel.sh config_cpsec.yaml newchannel peer1.org2.cpsec.com peer2.org3.cpsec.com

	command :=  constant.JoinPeers + constant.Separator + "config_" + channel.ChainName + constant.Yaml + constant.Separator + channel.ChannelName + allPeersName
	path, err := config_yaml.GetParentDirectory()
	if err != nil {
		return "Create channel failure", err
	}
	err = fautil.Exec_shell(command, filepath.Join(path, constant.Dirpath))
	if err != nil{
		logger.Error(err)
		return "Update yaml file failure", err
	}
	logger.Info("Peers join channel success!")
	/*操作fabric end*/
	return "Success", nil
}

//定义channel和构建
func (f FabricOperator) joinNewOrgToChannel(chain model.FabricChannelWithOrg, flag bool)  (string, error){

	newOrg := chain.PeersOrgs
	orgs := append(newOrg, chain.EndorserOrgs...)
	/**
	flag表示该组织是否已经加入过某个通道，是否需要更新配置文件，
	如果flag==true，则说明组织没有加如果某通道，即配置文件中没有该组织的信息
	*/

	if flag{
		command :=  constant.AddOrg + constant.Separator + "config_" + chain.ChainName + constant.Yaml +
			constant.Separator + chain.PeersOrgs[0]
		path, err := config_yaml.GetParentDirectory()
		if err != nil {
			return "Create channel failure", err
		}
		err = fautil.Exec_shell(command, filepath.Join(path, constant.Dirpath))
		if err != nil{
			logger.Error(err)
			return "Update yaml file failure", err
		}
	}

	fsdk := fasdk.NewFabricClient(chain.ChainName, chain.ChannelName, orgs,
		chain.OrdererName, chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}

	/***********************************
	动态添加新组织，需要通道内绝大多数组织的签名
	***********************************/
	err = fsdk.JoinNewOrgToChannel(filepath.Join(chain.WorkPath, newOrg[0]))
	if err != nil{
		logger.Error(err)
		return "Join new org failure", err
	}

	logger.Info("Peers join channel success!")
	/*操作fabric end*/
	return "Success", nil
}

func (f FabricOperator) removeExistedOrgFromChannel(chain model.FabricChannelWithOrg)  (string, error){

	newOrg := chain.PeersOrgs
	orgs := append(newOrg, chain.EndorserOrgs...)

	fsdk := fasdk.NewFabricClient(chain.ChainName, chain.ChannelName, orgs,
		chain.OrdererName, chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}

	/***********************************
	动态添加新组织，需要通道内绝大多数组织的签名
	***********************************/
	err = fsdk.RemoveExistedOrgFromChannel()
	if err != nil{
		logger.Error(err)
		return "Remove org failure", err
	}

	/**
	使用脚本更新sdk配置文件，删除通道內组织（节点）的所有信息 removeOrg.sh
	 */
	command := constant.RemoveChannel + constant.Separator + "config_" + chain.ChainName + constant.Yaml +
		constant.Separator + chain.PeersOrgs[0] + constant.Separator + chain.ChannelName
	path, err := config_yaml.GetParentDirectory()
	if err != nil {
		return "Remove org failure", err
	}
	err = fautil.Exec_shell(command, filepath.Join(path, constant.Dirpath))
	if err != nil{
		logger.Error(err)
		return "Update yaml file failure", err
	}
	logger.Info("Remove org success!")
	/*操作fabric end*/
	return "Success", nil
}

//定义channel和构建
func (f FabricOperator) joinNewOrgToSystemChannel(chain model.FabricChannelWithOrg)  (string, error){

	newOrg := chain.PeersOrgs
	orgs := append(newOrg, chain.EndorserOrgs...)

	fsdk := fasdk.NewFabricClient(chain.ChainName, chain.ChannelName, orgs,
		chain.OrdererName, chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}

	/***********************************
	动态添加新组织，需要通道内绝大多数组织的签名
	***********************************/
	err = fsdk.JoinNewOrgToSystemChannel(filepath.Join(chain.WorkPath, newOrg[0]))
	if err != nil{
		logger.Error(err)
		return "Join new org failure", err
	}
	/*操作fabric end*/
	return "Success", nil
}

func (f FabricOperator) removeExistedOrgFromSystemChannel(chain model.FabricChannelWithOrg)  (string, error){

	newOrg := chain.PeersOrgs
	orgs := append(newOrg, chain.EndorserOrgs...)

	fsdk := fasdk.NewFabricClient(chain.ChainName, chain.ChannelName, orgs,
		chain.OrdererName, chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}

	/***********************************
	动态添加新组织，需要通道内绝大多数组织的签名
	***********************************/
	err = fsdk.RemoveExistedOrgFromSystemChannel()
	if err != nil{
		logger.Error(err)
		return "Remove org failure", err
	}
	/*操作fabric end*/
	return "Success", nil
}

func (f FabricOperator)deployChaincode(channel model.FabricChannelAndChaincodeWithOrg) (string, error){
	//chain := channel.GetChain()
	/*操作fabric start*/

	fsdk := fasdk.NewFabricClient(channel.ChainName, channel.ChannelName,
		channel.PeersOrgs, channel.OrdererName, channel.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//安装chaincode
	ccPkg, err := fsdk.PackageCC(channel.ChaincodeName, channel.Language, channel.GithubPath)
	if err != nil {
		logger.Error(err.Error())
		return "Package chaincode failure", err
	}
	packageId, err := fsdk.InstallChaincode(channel.ChaincodeName, ccPkg)
	if err != nil {
		logger.Error(err.Error())
		return "Install chaincode failure", err
	}

	fsdk_ := fasdk.NewFabricClient(channel.ChainName, channel.ChannelName,
		channel.EndorserOrgs, channel.OrdererName, channel.ChaincodeSourcePath)
	defer fsdk_.Close()
	err = fsdk_.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	_, err = fsdk_.ApproveChaincode(channel.ChaincodeName, channel.Version, channel.Policy,
		packageId, 1, channel.ApprovePeers)
	if err != nil {
		logger.Error(err.Error())
		return "Approve chaincode failure", err
	}
	_, err = fsdk.CommitChaincode(channel.ChaincodeName, channel.Version, channel.Policy, 1, channel.EndorserPeers)
	if err != nil {
		logger.Error(err.Error())
		return "Commit chaincode failure", err
	}
	if err != nil {
		logger.Error(err)
		return "Package chaincode failure", err
	}

	logger.Info("Deploy chaincode success!")
	/*操作fabric end*/
	return "Deploy chaincode success", nil
}


func (f FabricOperator) updateChaincode(channel model.FabricChannelAndChaincodeWithOrg) (string, error){
	//chain := channel.GetChain()
	/*操作fabric start*/

	fsdk := fasdk.NewFabricClient(channel.ChainName,
		channel.ChannelName,
		channel.PeersOrgs,
		channel.OrdererName,
		channel.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//安装chaincode
	ccPkg, err := fsdk.PackageCC(channel.ChaincodeName, channel.Language, channel.GithubPath)
	if err != nil {
		logger.Error(err.Error())
		return "Package chaincode failure", err
	}
	_, err = fsdk.InstallChaincode(channel.ChaincodeName, ccPkg)
	if err != nil {
		logger.Error(err.Error())
		return "Install chaincode failure", err
	}
	packageId, err := fsdk.GetChaincodePackageId(channel.ChaincodeName, ccPkg)
	if err != nil {
		logger.Error(err.Error())
		return "Get chaincode failure", err
	}
	if packageId == "" {
		logger.Error(errors.New("package id is nil"))
		return "Install chaincode failure", err
	}
	sequence, err := fsdk.GetChaincodeSequence(channel.ChaincodeName, channel.OrgPeers)

	fsdk_ := fasdk.NewFabricClient(channel.ChainName, channel.ChannelName,
		channel.EndorserOrgs, channel.OrdererName, channel.ChaincodeSourcePath)
	defer fsdk_.Close()
	err = fsdk_.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	_, err = fsdk_.ApproveChaincode(channel.ChaincodeName, channel.Version, channel.Policy,
		packageId, sequence + 1, channel.ApprovePeers)
	if err != nil {
		logger.Error(err.Error())
		return "Approve chaincode failure", err
	}
	_, err = fsdk.CommitChaincode(channel.ChaincodeName, channel.Version, channel.Policy, sequence + 1, channel.EndorserPeers)
	if err != nil {
		logger.Error(err.Error())
		return "Commit chaincode failure", err
	}
	if err != nil {
		logger.Error(err)
		return "Package chaincode failure", err
	}
	logger.Info("Upgrade chaincode success!")
	/*操作fabric end*/
	return "Upgrade chaincode success!", nil
}

func (f FabricOperator) initChaincode(channel model.FabricChannelAndChaincodeWithOrg) (string, error){

	/*操作fabric start*/

	fsdk := fasdk.NewFabricClient(channel.ChainName,
		channel.ChannelName,
		channel.PeersOrgs,
		channel.OrdererName,
		channel.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	resp, err := fsdk.InitChaincode(channel.PeersOrgs[0],
		channel.EndorserPeers,
		channel.ChaincodeName,
		string(channel.ArgsGm[0]),
		channel.ArgsGm[1:])
	if err != nil {
		logger.Error(err)
		return "Invoke chaincode failure", err
	}
	logger.Info("Invoke chaincode success")
	logger.Info("Transaction ID" + string(resp))
	/*操作fabric end*/

	return string(resp), nil
}
func (f FabricOperator) invokeChaincode(channel model.FabricChannelAndChaincodeWithOrg) (string, error){
	//chain := channel.GetChain()
	/*操作fabric start*/
	fsdk := fasdk.NewFabricClient(channel.ChainName,
		channel.ChannelName,
		channel.PeersOrgs,
		channel.OrdererName,
		channel.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	resp, err := fsdk.InvokeChaincode(channel.ChaincodeName,
		channel.PeersOrgs,
		channel.OrgPeers,
		string(channel.ArgsGm[0]),
		channel.ArgsGm[1:])
	if err != nil {
		logger.Error(err)
		return "Invoke chaincode failure", err
	}
	logger.Info("Invoke chaincode success")
	logger.Info("Transaction ID" + string(resp))
	/*操作fabric end*/

	return string(resp), nil
}

func (f FabricOperator) queryChaincode(channel model.FabricChannelAndChaincodeWithOrg) (string, error){
	//chain := channel.GetChain()
	/*操作fabric start*/
	fsdk := fasdk.NewFabricClient(channel.ChainName,
		channel.ChannelName,
		channel.PeersOrgs,
		channel.OrdererName,
		channel.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "fabric sdk setup failure", err
	}
	//创建channel
	resp, err := fsdk.QueryChaincode(channel.ChaincodeName,
		channel.PeersOrgs[0],
		string(channel.ArgsGm[0]),
		channel.ArgsGm[1:],
		channel.OrgPeers)
	if err != nil {
		logger.Error(err)
		return "query chaincode failure", err
	}
	logger.Info(string(resp))
	/*操作fabric end*/
	return  string(resp), nil

}

func (f FabricOperator) queryLedger(chain model.FabricChannelWithOrg) (interface{}, error){

	/*操作fabric start*/
	fsdk := fasdk.NewFabricClient(chain.ChainName,
		chain.ChannelName,
		chain.PeersOrgs,
		chain.OrdererName,
		chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	resp, err := fsdk.QueryLedger(chain.OrgPeers)
	if err != nil {
		logger.Error(err)
		return "Query ledger failure", err
	}
	fabricBlock := new(fasdk.FabricBlock)
	fabricBlock.Number = resp.BCI.Height
	fabricBlock.PreviousBlockHash = hex.EncodeToString(resp.BCI.PreviousBlockHash)
	fabricBlock.CurrentBlockHash = hex.EncodeToString(resp.BCI.CurrentBlockHash)
	return  fabricBlock, nil
}

func (f FabricOperator) queryLatestBlocks(chain model.FabricChannelWithOrg) (interface{}, error){

	/*操作fabric start*/
	fsdk := fasdk.NewFabricClient(chain.ChainName,
		chain.ChannelName,
		chain.PeersOrgs,
		chain.OrdererName,
		chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	ledger, err := fsdk.QueryLedger(chain.OrgPeers)
	if err != nil {
		logger.Error(err)
		return "Query ledger failure", err
	}
	height := ledger.BCI.Height
	blocks := make([]*fasdk.FabricBlock,0)
	/*操作fabric end*/
	for i:= 0; height > 0;i++{
		height--
		if i > 10{
			break
		}
		block, err :=fsdk.QueryBlockByNumber(height, chain.OrgPeers)
		if err != nil {
			logger.Error(err)
			return "Query block failure", err
		}
		blocks = append(blocks,block)
	}
	return blocks, nil
}

func (f FabricOperator) queryBlockByNumber(chain model.FabricChannelWithOrg, blockId int) (interface{}, error){

	/*操作fabric start*/
	fsdk := fasdk.NewFabricClient(chain.ChainName,
		chain.ChannelName,
		chain.PeersOrgs,
		chain.OrdererName,
		chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	blocks := make([]*fasdk.FabricBlock,0)
	/*操作fabric end*/
	var block *fasdk.FabricBlock
	height := blockId
	if err == nil {
		block, err = fsdk.QueryBlockByNumber(uint64(height), chain.OrgPeers)
		if err != nil {
			logger.Error(err)
			return "Query block failure", err
		}
	}
	if block != nil {
		blocks = append(blocks, block)
	}
	return blocks, nil
}

func (f FabricOperator) queryBlockByHash(chain model.FabricChannelWithOrg, blockHash string) (interface{}, error){

	/*操作fabric start*/
	fsdk := fasdk.NewFabricClient(chain.ChainName,
		chain.ChannelName,
		chain.PeersOrgs,
		chain.OrdererName,
		chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	blocks := make([]*fasdk.FabricBlock,0)
	/*操作fabric end*/
	var block *fasdk.FabricBlock

	hash, err := hex.DecodeString(blockHash)
	if err != nil {
		logger.Error(err)
		return "Query block failure", err
	}
	block, err = fsdk.QueryBlockByHash(hash, chain.OrgPeers)
	if err != nil {
		logger.Error(err)
		return "Query block failure", err
	}

	if block != nil {
		blocks = append(blocks,block)
	}
	return blocks, nil
}

func (f FabricOperator) queryBlockByTxId(chain model.FabricChannelWithOrg, txId string) (string, error){

	/*操作fabric start*/
	fsdk := fasdk.NewFabricClient(chain.ChainName,
		chain.ChannelName,
		chain.PeersOrgs,
		chain.OrdererName,
		chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	blocks := make([]*fasdk.FabricBlock,0)
	/*操作fabric end*/
	var block *fasdk.FabricBlock

	if block == nil {
		block, err = fsdk.QueryBlockByTxid(txId, chain.OrgPeers)
		if err != nil {
			logger.Error(err)
			return "Query block failure", err
		}
	}

	if block != nil {
		blocks = append(blocks,block)
	}
	blocksJson, err := json.Marshal(blocks)
	if err != nil {
		logger.Error(err)
		return "Marshal blocks failure", err
	}
	return string(blocksJson), nil
}

func (f FabricOperator) QueryTransactionDetail(chain model.FabricChannelWithOrg, txId string) (interface{}, error){
	/*操作fabric start*/
	fsdk := fasdk.NewFabricClient(chain.ChainName,
		chain.ChannelName,
		chain.PeersOrgs,
		chain.OrdererName,
		chain.ChaincodeSourcePath)
	defer fsdk.Close()
	err := fsdk.Setup()
	if err != nil {
		logger.Error(err)
		return "Fabric sdk setup failure", err
	}
	//创建channel
	transaction := new(fasdk.FabricTransaction)
	/*操作fabric end*/

	transaction, err = fsdk.QueryTransactionDetail(txId, chain.OrgPeers)
	if err != nil {
		logger.Error(err)
		return "Query block failure", err
	}

	return transaction, nil
}

//func (f FabricOperator) GetFirstOrderer(chainName string) (string, error){
//	dbengine := xorm.GetEngine(config.Config.GetString("InterfaceGatewayDbconfig"))
//	ordererOperator := NewOrdererOperator(dbengine)
//	err, orderers := ordererOperator.GetAllByChain(chainName)
//	if err != nil {
//		logger.Error(err)
//		return "Get orderers failure", err
//	}
//	return orderers[0].OrdererName, nil
//}
func NewFabricOperator() *FabricOperator {
	return &FabricOperator{}
}